package com.linran.structure_algorithm.数据结构.lesson6_栈;

import org.springframework.util.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 使用栈完成表达式的计算思路
 * 1.通过一个index值(索引)，来遍历我们的表达式
 * 2.如果我们发现是- -个数字,就直接入数栈
 * 3.如果发现扫描到是一个符号,就分如下情况
 * 3.1如果发现当前的符号栈为空，就直接入栈
 * 3.2如果符号栈有操作符，就进行比较，如果当前的操作符的优先级小于或者等于栈中
 * 的操作符，就需要从数栈中pop出两个数在从符号栈中pop出-一个符号，进行运算，
 * 将得到结果，入数栈，然后将当前的操作符入符号栈，如果当前的操作符的优先级大
 * 于栈中的操作符，就直接入符号栈.
 * 4.当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行.
 * 5.最后在数栈只有一个数字, 就是表达式的结果
 */
public class CalculatorLearn {
    private static final Set<String> oprSet = Set.of("-", "+", "/", "*");
    // key数值越大代表优先级越高
    private static final Map<Integer, Set<String>> oprMap = new HashMap<>() {{
        Set<String> firstOprSet = Set.of("-", "+");
        Set<String> secondOprSet = Set.of("*", "/");
        put(1, firstOprSet);
        put(2, secondOprSet);
    }};

    private StackLearn<Integer> numStack;
    private StackLearn<String> oprStack;

    /*初始化*/
    public void init() {
        this.numStack = new StackLearn<>(10);
        this.oprStack = new StackLearn<>(10);
    }

    /**
     * 入口方法
     *
     * @param expression 1*2+3/1
     * @return
     */
    public Integer inputExpression(String expression) {
        if (StringUtils.isEmpty(expression)) {
            throw new RuntimeException("empty Str");
        }
        // 初始化
        init();
        // 提取元素
        List<String> elements = extractElement(expression);
        // 处理元素入栈
        elements.forEach(this::stackSaveDeal);
        // 递归处理算法
        finalDeal();
        //最终结果num stack保留最后一个结果值
        return Optional.ofNullable(numStack.firstElement())
                .orElseThrow(() -> new RuntimeException("返回结果~"));
    }

    public void stackSaveDeal(String element) {
        if (!oprSet.contains(element)) {
            //数字元素
            //StringUtils.isNumber
            numDeal(element);
        } else {
            //操作符
            operatorDeal(element);
        }
    }

    public void numDeal(String num) {
        //直接入栈
        if (StringUtils.isEmpty(num)) {
            return;
        } else {
            try {
                Integer.parseInt(num);
            } catch (Exception e) {
                System.out.println("非数字");
                return;
            }
        }
        numStack.push(Integer.parseInt(num));
    }

    public void operatorDeal(String operator) {
        //1.如果操作符栈为空直接入栈-保证数栈至少有两个数字和一个操作符
        if (StringUtils.isEmpty(oprStack.firstElement())) {
            oprStack.push(operator);
            return;
        }
        //2.如果操作符运算优先级小于等于操作符栈中的运算符，则pop数栈中两个数字并进行计算，讲结果存入数栈
        if (isPrecedenceStack(operator)) {
            pop2NumFigure();
        }
        //3.如果当前操作符运算优先级大于操作符栈中的运算符，则直接入栈
        oprStack.push(operator);
    }

    //pop两个数字并进行计算
    public void pop2NumFigure() {
        if (numStack.size() < 2 || oprStack.size() < 0) {
            throw new RuntimeException("[pop2NumFigure] condition is not met");
        }
        Integer numOne = numStack.pop();
        Integer numTwo = numStack.pop();
        String opr = oprStack.pop();
        int numRet = figure(numTwo, numOne, opr);
        numStack.push(numRet);
    }

    public boolean isPrecedenceStack(String curOpr) {
        String oprStr = oprStack.firstElement();
        if (StringUtils.isEmpty(curOpr)) {
            throw new RuntimeException("the oprStack is null");
        }
        Optional.ofNullable(oprStr).orElseThrow(() -> new RuntimeException("opr stack is empty"));
        int i = compareOpr(curOpr, oprStr);
        return i <= 0;
    }

    //比较优先级 0:代表相等 -1:代表opr1<opr2 1:代表opr1>opr2
    public int compareOpr(String opr1, String opr2) {
        if (StringUtils.isEmpty(opr1) && StringUtils.isEmpty(opr2)) {
            throw new RuntimeException("empty opr1 or opr2");
        }
        int oprPriority1 = getOprPriority(opr1);
        int oprPriority2 = getOprPriority(opr2);
        return Integer.compare(oprPriority1, oprPriority2);
    }

    /*获取运算符优先级*/
    private int getOprPriority(String opr) {
        if (Objects.isNull(opr)) {
            throw new RuntimeException("empty opr");
        }
        for (Integer priority : oprMap.keySet()) {
            if (oprMap.get(priority).contains(opr)) {
                return priority;
            }
        }
        throw new RuntimeException(String.format("can't find priority, %s", opr));
    }

    private int figure(Integer num1, Integer num2, String opr) {
        if (Objects.isNull(opr) && Objects.isNull(num1) && StringUtils.isEmpty(num2)) {
            throw new RuntimeException("empty parameter");
        }
        int ret;
        switch (opr) {
            case "-":
                ret = num1 - num2;
                break;
            case "+":
                ret = num1 + num2;
                break;
            case "*":
                ret = num1 * num2;
                break;
            case "/":
                ret = num1 / num2;
                break;
            default:
                throw new RuntimeException("opt error");
        }
        return ret;
    }

    private List<String> extractElement(String expression) {
        List<String> list = new ArrayList<>();
        if (StringUtils.isEmpty(expression)) {
            return Collections.emptyList();
        }
        List<String> numList = extractNum(expression);
        List<String> oprList = extractOpr(expression);
        int index = 0;
        for (String s : numList) {
            // i为奇数
            list.add(s);
            if (index != (numList.size() - 1)) {
                list.add(oprList.get(index));
            }
            index++;
        }
        return list;
    }

    private List<String> extractNum(String expression) {
        String[] nums = expression.split("[+\\-*/]");
        if (nums.length < 2) {
            throw new RuntimeException("illegal expression");
        }
        return Arrays.asList(nums);
    }

    private List<String> extractOpr(String expression) {
        List<String> oprList = new ArrayList<>();
        Pattern compile = Pattern.compile("[+\\-*/]");
        Matcher matcher = compile.matcher(expression);
        while (matcher.find()) {
            String group = matcher.group(0);
            oprList.add(group);
        }
        return oprList;
    }

    /*最终处理*/
    private void finalDeal() {
        while (oprStack.size() > 0) {
            pop2NumFigure();
        }
    }

    public static void main(String[] args) {
        //只能进行简单的运算。。。  char和int是相通的
        String expression = "1+2*5-3";
        CalculatorLearn calculatorLearn = new CalculatorLearn();
        Integer integer = calculatorLearn.inputExpression(expression);
        System.out.printf("结果:%s%n", integer);
    }
}
